
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
#include "demo.h"
#include <linux/delay.h>

#define MAJOR_NUM 123
#define MINOR_NUM 0
#define IN_BUF_LEN 256
#define OUT_BUF_LEN 512

MODULE_AUTHOR("mdy");
MODULE_DESCRIPTION("ioctl demo");

static struct class * hello_class;
static struct cdev hello_cdev;
static dev_t devnum = 0;
static char * modname = "hello_mod";
static char * devicename = "hello";
static char * classname = "hello_class";
static int g_num = 0;

static int open_count = 0;
static struct semaphore sem;
static DEFINE_SPINLOCK(spin);

static int hello_mod_open(struct inode *, struct file *);
static int hello_mod_release(struct inode *, struct file *);
static long hello_mod_ioctl(struct file *, unsigned int, unsigned long);

struct file_operations hello_mod_fops =
{
    .owner = THIS_MODULE,
    .open = hello_mod_open,
    .unlocked_ioctl = hello_mod_ioctl,
    .release = hello_mod_release,
};

static int hello_mod_open(struct inode *inode, struct file *pfile)
{
    printk("+hello_mod_open()!\n");
    spin_lock(&spin);
    if(open_count)
    {
        spin_unlock(&spin);
        return -EBUSY;
    }
    open_count++;
    spin_unlock(&spin);
    printk("-hello_mod_open()!\n");
    return 0;
}
static int hello_mod_release(struct inode *inode, struct file *pfile)
{
    printk("+hello_mod_release()!\n");
    open_count--;
    printk("-hello_mod_release()!\n");
    return 0;
}

static long hello_mod_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
    int err = 0;
    switch(cmd)
    {
        case HELLO_IOCTL_INC_NUM:
	        g_num++;
            break;
        case HELLO_IOCTL_PRINT_NUM:
	        printk("g_num = %d\n", g_num);
	        break;
        case HELLO_IOCTL_DELAYTEST:
            //试试看delay和sleep的效果区别
            //mdelay(arg);
            msleep(arg);
            break;
        default:
            printk("        >in case: default\n");
            err = ENOTSUPP;
            break;
    }
    return err;
}

static int __init hello_mod_init(void)
{
    int result;
    printk("+hello_mod_init()!\n");
    devnum = MKDEV(MAJOR_NUM, MINOR_NUM);
    result = register_chrdev_region(devnum, 1, modname);

    if(result < 0)
    {
        printk("hello_mod : can't get major number!\n");
        return result;
    }

    cdev_init(&hello_cdev, &hello_mod_fops);
    hello_cdev.owner = THIS_MODULE;
    hello_cdev.ops = &hello_mod_fops;
    result = cdev_add(&hello_cdev, devnum, 1);
    if(result)
        printk("Failed at cdev_add()");
    hello_class = class_create(THIS_MODULE, classname);
    if(IS_ERR(hello_class))
    {
        printk("Failed at class_create()\n");
    }
    else
    {
        device_create(hello_class, NULL, devnum,NULL, devicename);
    }

    open_count = 0;
    sema_init(&sem, 1);
    printk("-hello_mod_init()!\n");
    return 0;
}

static void __exit hello_mod_exit(void)
{
    printk("+hello_mod_exit!\n");
    cdev_del(&hello_cdev);
    device_destroy(hello_class, devnum);
    class_destroy(hello_class);
    unregister_chrdev_region(devnum, 1);
    printk("-hello_mod_exit!\n");
    return ;
}

module_init(hello_mod_init);
module_exit(hello_mod_exit);
MODULE_LICENSE("GPL");

